package cn.org.wangchangjiu.sqltomongo.core.analyzer.select_option;

import cn.org.wangchangjiu.sqltomongo.core.adapter.MyExpressionVisitorAdapter;
import cn.org.wangchangjiu.sqltomongo.core.parser.data.*;
import cn.org.wangchangjiu.sqltomongo.core.util.SqlCommonUtil;
import net.sf.jsqlparser.expression.CaseExpression;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.WhenClause;
import org.apache.commons.collections4.CollectionUtils;
import org.bson.Document;

import java.util.*;

/**
 * 表达式分析
 */
public class ExpressionAnalyzer {

    public static final String FUNC_PRE_STR = "func_";

    public static Document buildCaseExpr(CaseExpressionData caseExpressionData, TableNameManager tableNameManager) {
        Document document = new Document();
        List<Document> branches = new ArrayList<>();
        List<WhenThenData> whenThenDataList = caseExpressionData.getWhenThenDataList();
        if (!CollectionUtils.isEmpty(whenThenDataList)) {
            for (WhenThenData whenThenData : whenThenDataList) {
                Document when = new Document();
                when.put("case",buildExpressions(whenThenData.getMatchExpression().getMatchDataList(),tableNameManager,true));
                when.put("then", tableNameManager.addTableName(whenThenData.getThenValue()));
                branches.add(when);
            }
        }
        Document branches1 = new Document("branches", branches);
        branches1.put("default",tableNameManager.addTableName(caseExpressionData.getElseValue()));
        document.put("$switch", branches1);
        return document;
    }

    public static Document buildExpressions(List<MatchData> matchData, TableNameManager tableNameManager, boolean isCaseExpr) {
        Deque<Document> stack = new ArrayDeque<>();
        if (!CollectionUtils.isEmpty(matchData)) {
            // matchData 已经安装优先级排序了 这里不需要处理优先级
            matchData.stream().forEach(matchDataItem -> {
                if (!matchDataItem.getIsOperator()) {
                    // 不是操作符 入栈
                    MatchData.RelationExpressionItem expression = (MatchData.RelationExpressionItem) matchDataItem.getExpression();
                    String operator = expression.getOperator();
                    String field = matchDataItem.getField(tableNameManager);
                    Object paramValue = expression.getValue();

                    Document where = ExpressionAnalyzer.buildExpr(field,operator,paramValue,isCaseExpr);
                    stack.push(where);
                } else {
                    MatchData.OperatorExpressionItem expression = MatchData.OperatorExpressionItem.class.cast(matchDataItem.getExpression());
                    // 操作符 取出两个 操作数 来计算
                    Document left = stack.pop();
                    Document right = stack.pop();
                    Document result = null;
                    if ("AND".equalsIgnoreCase(expression.getOperatorName())) {
                        result = new Document("$and", Arrays.asList(left, right));
                    } else if ("OR".equalsIgnoreCase(expression.getOperatorName())) {
                        result = new Document("$or", Arrays.asList(left, right));
                    }
                    stack.push(result);
                }
            });
        }
        return stack.isEmpty()?null:stack.getFirst();
    }

    public static Document buildExpr(String field, String operator, Object paramValue, boolean isCaseExpr) {
        Document expr = new Document();
        if ("=".equals(operator)) {
            if(isCaseExpr){
                expr.append("$eq", Arrays.asList("$".concat(field),paramValue));
            }else{
                expr.append(field, paramValue);
            }
        } else if ("LIKE".equalsIgnoreCase(operator)) {
            String regex = String.valueOf(paramValue).replace("%", ".*").replace("_", ".");
            if(isCaseExpr){
                Document value = new Document();
                value.put("input","$".concat(field));
                value.put("regex", regex);
                expr.append("$regexMatch", value);
            }else if (paramValue instanceof String) {
                expr.append(field, new Document("$regex", regex));
            }
        } else if ("IN".equalsIgnoreCase(operator) && paramValue instanceof Collection) {
            Collection collection = (Collection) paramValue;
            if (isCaseExpr) {
                expr.append("$in", Arrays.asList("$".concat(field), collection));
            }else {
                expr.append(field, new Document("$in", collection));
            }
        }else if ("NOT IN".equalsIgnoreCase(operator) && paramValue instanceof Collection) {
            Collection collection = (Collection) paramValue;
            if (isCaseExpr){
                expr.append("$not",new Document("$in", Arrays.asList("$".concat(field), collection)));
            }else {
                expr.append(field, new Document("$nin", collection));
            }
        } else if ("<".equals(operator)) {
            if (isCaseExpr) {
                expr.append("$lt", Arrays.asList("$".concat(field), paramValue));
            }else{
                expr.append(field, new Document("$lt", paramValue));
            }
        } else if ("<=".equals(operator)) {
            if (isCaseExpr) {
                expr.append("$lte", Arrays.asList("$".concat(field), paramValue));
            }else{
                expr.append(field, new Document("$lte", paramValue));
            }
        } else if (">".equals(operator)) {
            if (isCaseExpr) {
                expr.append("$gt", Arrays.asList("$".concat(field), paramValue));
            }else{
                expr.append(field, new Document("$gt", paramValue));
            }
        } else if (">=".equals(operator)) {
            if (isCaseExpr) {
                expr.append("$gte", Arrays.asList("$".concat(field), paramValue));
            }else{
                expr.append(field, new Document("$gte", paramValue));
            }
        } else if ("!=".equals(operator) || "<>".equals(operator)) {
            if (isCaseExpr) {
                expr.append("$ne", Arrays.asList("$".concat(field), paramValue));
            }else {
                expr.append(field, new Document("$ne", paramValue));
            }
        }
        return expr;
    }

    public static CaseExpressionData analyzerCaseExpression(CaseExpression caseExpression) {
        CaseExpressionData caseExpressionData = new CaseExpressionData();
        //解析when then组合 列表
        ArrayList<WhenThenData> whenThenData = new ArrayList<>();
        List<WhenClause> whenClauses = caseExpression.getWhenClauses();
        if (!CollectionUtils.isEmpty(whenClauses)) {
            for (WhenClause whenClause : whenClauses) {
                WhenThenData thenData = new WhenThenData();
                //when表达式
                Expression whenExpression = whenClause.getWhenExpression();
                List<MatchData> matchData = new ArrayList<>();
                whenExpression.accept(new MyExpressionVisitorAdapter(null, matchData));
                MatchExpression matchExpression = new MatchExpression(matchData);
                thenData.setMatchExpression(matchExpression);
                //解析when value
                thenData.setThenValue(SqlCommonUtil.handleExpressionValue(whenClause.getThenExpression()));
                whenThenData.add(thenData);
            }
        }
        caseExpressionData.setWhenThenDataList(whenThenData);
        //解析else value
        caseExpressionData.setElseValue(SqlCommonUtil.handleExpressionValue(caseExpression.getElseExpression()));
        return caseExpressionData;
    }
}
